#include "player.h"

#include "../camera.h"  // e3dCamera
#include "../input.h"   // eInput
#include "../console.h" // eTB_Printf

#include "level.h"
#include "room.h"
#include "sector.h"
#include "line.h"

#include "states.h"

#include "bsp.h"  // eTB_Polygon
#include "util.h" // UnderlightAngle (...)

#include "effect.h" // effects->GetSound (...)

#ifndef _MSC_VER
# include <set.h> /* std::set */
#else
# include <set>
#endif

void
cli_parse::Player::SetInput (eInput* input)
{
  this->input = input;
}

eInput*
cli_parse::Player::Input (void)
{
  return input;
}

void
cli_parse::Player::SetCamera (e3dCamera* camera)
{
  cam = camera;
}

e3dCamera*
cli_parse::Player::Camera (void)
{
  return cam;
}


#include "util.h"
#include "../sound.h"

OpenALSource* sound_source   = NULL;
OpenALBuffer* teleport_sound = NULL;

void
cli_parse::Player::Teleport (int room, float x, float y, float angle, float z)
{
  if (! teleport_sound) {
    teleport_sound = MakeSoundAL (12);

    sound_source = states->sound_context->createSource ();
    sound_source->attach (teleport_sound);
  }

  sound_source->play ();

  if (! cam)
    return;

  sector = NULL;

  // Calculate the Z coordinate if it is -1000.0f
  if (z == -1000.0f) {
    z = 0.0f;

    //  TODO
    // ...

    sector = Underlight::lvl->FindSector (x, y, sector, true);
    //sector = FindSector (x, y, sector, true);

    if (sector) {
      Underlight::Room* room_ = Underlight::lvl->GetRoom (room);

      if (room_) {
        z = sector->FloorHt (x, y);
      }
    }

    z += 33.3f;
  }

  // Load the room
  //states->cl_cam_room = 0;
  //Underlight::Room* room_ = Underlight::lvl->GetRoom (room);
  //if (room_)
    //room_->Load (Underlight::lvl);

  on_ground = false;

  this->room = room;
  cam->SetPos (x, y, z);

  cam->SetRot (180 - Underlight::UnderlightAngle ((short)angle));

  SetTeleporting (true);
}

void
cli_parse::Player::SetTeleporting (bool teleporting)
{
  states->r_global_brightness = 0.0f;

  is_teleporting = teleporting;
}

bool
cli_parse::Player::Teleporting (void)
{
  return is_teleporting;
}



cli_parse::Player::Player (void)
{
  //cli_parse::Actor ();

  cam   = NULL;
  input = NULL;

  is_teleporting = false;
  on_ground      = false;
}

#if 0
#include "util.h"

namespace Underlight
{
const float MANUAL_TRIP_DISTANCE = 128.0f;
const int HITMAX = 128;

const float BUFFER_DIST = 75.0f;
const int MAX_SLIDES = 3;
const unsigned int DONT_INTERSECT	 = 0;
const unsigned int DO_INTERSECT		 = 1;
const unsigned int COLLINEAR			 = 0;
const int NO_INITIATES = 4; // for teleportal locking

enum hit_type {
	NO_HIT = 0,				// clear move
	HIT_ACTOR = 1,          // hit actor
	HIT_WALL = 2,           // hit wall
	HIT_FLOOR = 3,			// hit the floor
	HIT_CEILING = 4,		// hit the ceiling
	TELEPORTED = 5,
	HIT_JUMPABLE = 6,		// hit a wall that's jumpable (for agents)
};

enum move_type {
	MOVE_NORMAL = 1, // normal move
	MOVE_CHECK = 2, // check for collisions only
	MOVE_TRIP = 3,  // check for manual trips only
	MOVE_FIND_TELEPORTAL = 4, // find a teleportal
};

enum trip_message_type {
	NO_TRIP_MESSAGE = 0,
	TRAIN_JUMP,
	TRAIN_TELEPORTAL,

	TRAIN_CHAT_START,
	TRAIN_TALK,
	TRAIN_SHOUT,
	TRAIN_WHISPER,
	TRAIN_EMOTE,
	TRAIN_BOW,
	TRAIN_CHAT_FINISH,
	
	TRAIN_SKILLS_START, // 10
	TRAIN_SKILLS_SECOND,
	TRAIN_SKILLS_THIRD,
	TRAIN_SKILLS_KNOW,
	TRAIN_SKILLS_MEDITATE,
	TRAIN_SKILLS_TRAIL,
	TRAIN_SKILLS_DRAIN_NIGHTMARE,
	TRAIN_SKILLS_RANDOM,
	TRAIN_SKILLS_FOCUS,
	TRAIN_SKILLS_JOINPARTY,
	TRAIN_SKILLS_FINISH, // 20
	
	TRAIN_INVENTORY_START,
	TRAIN_INVENTORY_MID,
	TRAIN_INVENTORY_FINISH,
	
	TRAIN_HOUSES_START,
	TRAIN_HOUSES_MOON,
	TRAIN_HOUSES_ECLIPSE,
	TRAIN_HOUSES_SHADOW,
	TRAIN_HOUSES_COVENANT,
	TRAIN_HOUSES_RADIANCE,
	TRAIN_HOUSES_CALENTURE, // 30
	TRAIN_HOUSES_ENTRANCED,
	TRAIN_HOUSES_LIGHT,

	TRAIN_FOLLOW_TORCHES,
	TRAIN_SKILLS_GIVE,
	TRAIN_SKILLS_MAJORMINOR,
	TRAIN_SKILLS_POWER,
	TRAIN_SKILLS_ORBIT,
	TRAIN_HOUSES_CITYMAP,
	TRAIN_HOUSES_CONSIDER,
	TRAIN_HOUSES_FINDTEACHER, // 40
	TRAIN_HOUSES_TRAINAGAIN,
	
	TRAIN_SANCTUARY_START,
	TRAIN_SANCTUARY_FINISH,
	TRAIN_SKILLS_LOCATE_AVATAR, 
	TRAIN_INVENTORY_EXIT,

	LIBRARY_ENTRANCE,

	TRAIN_SKILLS_SENSE,
	TRAIN_BELIEF_MOON,
	TRAIN_BELIEF_ECLIPSE,
	TRAIN_BELIEF_SHADOW, //50
	TRAIN_BELIEF_COVENANT,
	TRAIN_BELIEF_RADIANCE,
	TRAIN_BELIEF_CALENTURE,
	TRAIN_BELIEF_ENTRANCED,
	TRAIN_BELIEF_LIGHT, //55

	TRAIN_QUEST_BUILDER1,
	TRAIN_QUEST_BUILDER2

};


// For reporting the results of a move
struct move_result_t
{
  float dist;
  int   hit;
  Line* l;
};

struct hit_t
{
  Line *l;
  int   bound;
  float    x;
  float    y;
  float  slide_x; // suggested position for sliding on walls
  float  slide_y;
  float slide_distance;
  float dist;
};

// Parameters for current move
struct move_params_t
{
  float      oldx;
  float      oldy;
  float      newx;
  float      newy;
  float      inter_x;
  float      inter_y;
  float      movedist;
  float        moveangle;
  float        origangle;
//  int        moveangle;
//  int        origangle;
  float      perps2;
  float      perpc2;
  int        slide_count;
  int        numhits;
  int        numbufferzones;
  bool       corner_tested;
  Line      *ignorecorner;
  hit_t      hits[HITMAX];
  cli_parse::Actor    *ouractor;
  BOOL       backwards;
  int        move_type;
};

int OnRight (Line* aLine, float Px, float Py)
{
	// these are all normalized and rotated, in respect to us.
	float x1,y1,x2,y2,temp;

	x1 = aLine->x1();
	y1 = aLine->y1();
	x2 = aLine->x2();
	y2 = aLine->y2();

	temp = ((x1-x2)*(Py-y2))-((y1-y2)*(Px-x2));

	if ( temp < 0 ) return 1;	// not on right!
	return 0;
}

// addsec is sector to be added; m->ouractor is moving into addsec from currsec
void AddLines(int addsec, int currsec, move_params_t *m, unsigned char *seclist)
{
	Underlight::Line*   ourline;
	Underlight::Sector* moveSector;
	int bound;

	// Don't queue up a sector more than once.
	if ( seclist[addsec] ) return;
	seclist[addsec]=1;

        extern Level* lvl;
	moveSector	= lvl->GetSector (addsec);
	if(!moveSector)  return;

///	if (!(m->ouractor->flags & ACTOR_SOULSPHERE))
///		AddActors(addsec, currsec, m, seclist);

	ourline = moveSector->firstline;
	if(!ourline) return;

	do {
		bound = 0;
		if (OnRight (ourline, m->ouractor->GetX (), m->ouractor->GetY ()))
		{// new stuff starts here
			bound = InsertL(ourline,currsec,m,seclist);
		// if we're very close to the line, and it's non-bounding and leading
		// to a different sector, add the other sector's lines
		if ((ourline->facingsector != ourline->sector) &&
			(LineDistance(m->newx,m->newy,ourline) < BUFFER_DIST) &&
			!bound)
			AddLines (ourline->facingsector->id, m->ouractor->sector->id, m, seclist);

		}
	} while(ourline=ourline->nextline);
}

int InsertL(Line* l, int currsec, move_params_t *m, unsigned char *seclist)
{
	Sector* dest;
	Sector* origin;
	int bound=0,hit=0;
	float newdist,olddist;
	BOOL addhit;
	float x1,y1,x2,y2,xd,yd,bufx1,bufx2,bufy1,bufy2;

	float mdx = BUFFER_DIST*CosTable[m->moveangle];
	float mdy = BUFFER_DIST*SinTable[m->moveangle];

	// determine whether we can pass the line or not.
	if (l->flags & BOUND)
		bound = 1;
	if ((l->flags & LINE_S_IMPASS) && (m->ouractor->Type() != MISSILE))
	  bound = 1; //super_impass always bounds non-missile actors
	else if (l->actor)
	{
		if (!(l->actor->flags & ACTOR_NOCOLLIDE))
		{	 // bounds if our bottom is below actor's top and our top is above actor's bottom
			if (((m->ouractor->z - m->ouractor->physht) <= (l->actor->z)) &&
				(m->ouractor->z >= (l->actor->z - l->actor->physht)))
				bound = 1;
		}
	}
	else if (l->facingsector != currsec)
	{
		dest = level->Sectors[l->facingsector];
		origin = level->Sectors[currsec];

		int hitline = lines_intersect(m, m->oldx, m->oldy, m->newx+mdx, m->newy+mdy, l->x1(), l->y1(), l->x2(), l->y2());

		float check_x, check_y;

		// pick the point at which to take floor and ceiling ht measurements
		if (hitline)
		{
			check_x = m->inter_x; check_y = m->inter_y;
		}
		else
		{
			if (  sortdist(m->newx, m->newy, l->x1(), l->y1())
				 < sortdist(m->newx, m->newy, l->x2(), l->y2()) )
			{
				check_x = l->x1(); check_y = l->y1();
			}
			else
			{
				check_x = l->x2(); check_y = l->y2();
			}
		}

		if (m->ouractor->z >= dest->CeilHt(check_x,check_y))
			bound =1; // actor hits ceiling

		else	if  ((m->ouractor->Type() == MISSILE)
				&& ((m->ouractor->z - m->ouractor->physht) <= dest->FloorHt(check_x,check_y)))
					bound = 1; // missile hits floor (no floor offset for missiles!)

		else	if (((origin->CeilHt(check_x, check_y) - (dest->FloorHt(check_x, check_y)+dest->HtOffset)) <= m->ouractor->physht)
				|| ((dest->CeilHt(check_x, check_y) - (origin->FloorHt(check_x, check_y)+origin->HtOffset)) <= m->ouractor->physht)
				|| ((origin->CeilHt(check_x, check_y) - (origin->FloorHt(check_x, check_y)+origin->HtOffset)) <= m->ouractor->physht)
				|| ((dest->CeilHt(check_x, check_y) - (dest->FloorHt(check_x, check_y)+dest->HtOffset)) <= m->ouractor->physht))
			bound = 1; // not enough vertical space for actor at boundary

		else	if ((m->ouractor->Type() != MISSILE)
				&& (dest->FloorHt(check_x,check_y)+dest->HtOffset > (origin->FloorHt(check_x, check_y) - origin->FloorHt(m->ouractor->x, m->ouractor->y) + m->ouractor->z - (m->ouractor->physht*.75))))
				// should no longer be needed, preserved until significant testing
//				|| (!hitline && (dest->FloorHt(m->oldx+mdx,m->oldy+mdy)+dest->HtOffset > (m->ouractor->z  - (m->ouractor->physht*.75))))))
			bound = 1; // too high for non-missile actor to step up into

		else if ((m->ouractor->Type() != MISSILE) && (m->move_type == MOVE_NORMAL))
			// if move to new sector is not bound, make an update to the move's z param to allow stair-climbing
		{	// only if an upwards move -- must let gravity affect the downward check
			if (hitline && (origin->FloorHt(m->inter_x, m->inter_y)+origin->HtOffset < dest->FloorHt(m->inter_x, m->inter_y)+dest->HtOffset)
			&& (LineDistance(m->newx, m->newy, l) < 12) 
			&& (m->ouractor->z < (origin->FloorHt(m->ouractor->x, m->ouractor->y)+(1.1*m->ouractor->physht))) 
			&& (m->ouractor->z > (origin->FloorHt(m->ouractor->x, m->ouractor->y)+(.9*m->ouractor->physht))))
			{
				m->ouractor->z = dest->FloorHt(m->inter_x,m->inter_y) + dest->HtOffset + m->ouractor->physht;
			}
		}
	}

	// get distance to line from m->newx and m->newy.
	// if less than bufferzone, lets also enter this line.
	if ( bound )
	{
		xd = BUFFER_DIST*l->COS;
		yd = BUFFER_DIST*l->SINE;

		// find the buffer zone line
		bufx1 = l->x1() - xd - yd;
		bufx2 = l->x2() + xd - yd;
		bufy1 = l->y1() - yd + xd;
		bufy2 = l->y2() + yd + xd;

		// Movements for the missile and non-missile actors are
		// handled differently.

		// missile:
		//		* If a missile actor crosses a physical line, a
		//		  bounding hit is recorded at the current location.
		//		* If the non-missile crosses the buffer zone, a bounding hit
		//		  is recorded at the point of crossing.A recommended slide
		//		  location is then put into the hit, and a recursive call
		//		  to VectorMove may result.
		//		* If the non-missile is in a buffer zone, a bounding hit is
		//		  recorded at the current location if the movement doesn't
		//		  move the non-missile farther from the line. A recommended
		//		  slide location is then put into the hit, as above.

		if (m->ouractor->IsMissile())
		{	// check three lines for hits - one from the current location
			// to the left edge of the new locaiton, one from the current
			// location to the right edge of the new location, one from current
			// location to the center of the new location
			if ((lines_intersect(m, m->oldx,m->oldy,
				(m->newx + m->ouractor->halfwit*CosTable[FixAngle(m->ouractor->angle + Angle_90)]),
				(m->newy + m->ouractor->halfwit*SinTable[FixAngle(m->ouractor->angle + Angle_90)]),
				l->x1(),l->y1(),l->x2(),l->y2())) ||
				(lines_intersect(m, m->oldx,m->oldy,
				(m->newx + m->ouractor->halfwit*CosTable[FixAngle(m->ouractor->angle - Angle_90)]),
				(m->newy + m->ouractor->halfwit*SinTable[FixAngle(m->ouractor->angle - Angle_90)]),
				l->x1(),l->y1(),l->x2(),l->y2())) ||
				(lines_intersect(m, m->oldx,m->oldy,m->newx,m->newy,l->x1(),l->y1(),l->x2(),l->y2())))
			{ // offset back by COLLIDE_OFFSET, after calculating middle intersect pos
					//convenient debugging stuff
//			if ((m->ouractor->Type() == MISSILE) &&
//				((l->from == 25) || (l->from == 28)) && ((l->to == 25) || (l->to == 28)))
//				int junk = 0;

				if (lines_intersect(m, m->oldx,m->oldy,m->newx+mdx,m->newy+mdy,l->x1(),l->y1(),l->x2(),l->y2()))
					AddHit(m, l, bound, sortdist(m->oldx,m->oldy,m->inter_x,m->inter_y),
							m->inter_x, m->inter_y, m->inter_x, m->inter_y, 0.0f);
				else // hitting the edge of something...
					if (sortdist(m->oldx, m->oldy, l->x1(), l->y1()) < sortdist(m->oldx, m->oldy, l->x2(), l->y2()))
						AddHit(m, l, bound, sortdist(m->oldx, m->oldy, l->x1(), l->y1()),
							l->x1(), l->y1(), l->x1(), l->y1(), 0.0f);
					else
						AddHit(m, l, bound, sortdist(m->oldx, m->oldy, l->x2(), l->y2()),
							l->x2(), l->y2(), l->x2(), l->y2(), 0.0f);
			}
		}
		else // need to slide...
		{
			addhit = FALSE; // to determine if we need a hit or not

			olddist = LineDistance(m->oldx,m->oldy,l);
			newdist = LineDistance(m->newx,m->newy,l);

			if ((olddist >= 0.0f) && (olddist <= BUFFER_DIST) && (On_Line(m->oldx,m->oldy,xd,yd,l)))
			{ // we're between the line and the buffer line
				m->numbufferzones++;

				if ((int)newdist < (int)olddist)
				{ // don't let get closer to the wall if already in bufferzone
					addhit = TRUE;
					x1 = m->oldx;
					y1 = m->oldy;
				}
			}
			else
			{
				if (lines_intersect(m, m->oldx,m->oldy,m->newx,m->newy,bufx1,bufy1,bufx2,bufy2))
				{
					x1 = m->inter_x;
					y1 = m->inter_y;
					addhit = TRUE;
				}
			}
			if (addhit)
			{	// don't slide off missiles
				if (l->actor && l->actor->Type() == MISSILE)
				{
					x2 = x1;
					y2 = y1;
				}
				else	if (	(((abs((FixAngle(m->moveangle+Angle_90)) - (GetFacingAngle(l->x1(),l->y1(),l->x2(),l->y2())))) < 24)
							|| ((abs((FixAngle(m->moveangle+Angle_90)) - (GetFacingAngle(l->x1(),l->y1(),l->x2(),l->y2())))) > 1000))
						||
								(((abs((FixAngle(m->moveangle+Angle_90)) - (GetFacingAngle(l->x2(),l->y2(),l->x1(),l->y1())))) < 24)
							|| ((abs((FixAngle(m->moveangle+Angle_90)) - (GetFacingAngle(l->x2(),l->y2(),l->x1(),l->y1())))) > 1000)))
				{ // don't slide if impact with wall is fairly flat
					x2 = x1;
					y2 = y1;
				}
				else if ((((m->oldx + m->movedist*CosTable[m->moveangle]) - m->oldx)*(l->x2() - l->x1()) + ((m->oldy + m->movedist*SinTable[m->moveangle]) - m->oldy)*(l->y2() - l->y1())) > 0)
				{ // angling the same way...
					x2 = x1 + m->movedist*l->COS;
					y2 = y1 + m->movedist*l->SINE;
				}
				else
				{ // opposite
					x2 = x1 - m->movedist*l->COS;
					y2 = y1 - m->movedist*l->SINE;
				}
				AddHit(m,l,bound,sortdist(m->oldx,m->oldy,x1,y1),x1,y1,x2,y2,(m->movedist - fdist(m->oldx, m->oldy, x1, y1)));
			}
		}
	}

	if ( !bound && !l->actor)
	{
		if (lines_intersect(m, m->oldx,m->oldy,m->newx,m->newy,l->x1(),l->y1(),l->x2(),l->y2()))
		{
			AddHit(m,l,bound,sortdist(m->oldx,m->oldy,m->inter_x,m->inter_y));
			AddLines(l->facingsector, m->ouractor->sector,m,(unsigned char*)seclist);
			//_tprintf("non-bounding sector cross from %d to %d\n",currsec,l->facingsector);
		}
	}
	return bound;
}



void VectorMove(move_params_t *m, move_result_t *result = NULL)
{
  Line  *l;
  hit_t *h;
  int i,newsector,slideangle;
  unsigned char seclist [MAX_SECTORS];
  Sector *dest;

  // Clean the sectors so we can check whether to queue them again or not.
  memset(seclist,0,MAX_SECTORS);
  m->numhits = 0;
  m->numbufferzones = 0;

  AddLines(m->ouractor->sector->id, m->ouractor->sector->id, m, (unsigned char*)seclist);

  // sort the lines if we have more than 1 hit.
  if ( m->numhits > 1)
     qsort(&(m->hits[0]),m->numhits,sizeof(hit_t),SortHits);

  // we have a sorted list of the lines that our path crossed, in order of distance.
  for ( i=0;i<m->numhits;i++ )
  {
    // get the hit and the linedef.
    h=&(m->hits[i]);
    l=h->l;

    if (m->move_type == MOVE_FIND_TELEPORTAL)
    {
      if (l->TripFlags & TRIP_TELEPORT)
      {
        if (result)
        {
           result->hit = TELEPORTED;
           result->l = l;
           result->dist = h->dist;
        }
        return;
      }
      else // go to next hit
        continue;
    }

    if (m->ouractor->IsPlayer())
    {
      // check for trips
      if (m->move_type == MOVE_TRIP)
      {
        if ((l->flags & TRIP_ACTIVATE) && (PlayerTripLine(l) == TELEPORTED))
          return;
        else // go to next hit
          continue;
      }
      else if (m->move_type == MOVE_NORMAL)
      {
        if ((l->flags & TRIP_CROSS) &&   (PlayerTripLine(l) == TELEPORTED))
        {
          if (result)
          {
            result->hit = TELEPORTED;
            result->dist = h->dist;
            result->l = l;
          }
          return;
        }
      }
    }

    if ( h->bound)
    {
      // if line was already ignored as a result of corner checking, ignore again
      if (l == m->ignorecorner)
        continue;

      // if multiple bounding hits, check to see if actor is in a corner
      // or stuck on the corner of two walls
      if ((m->move_type == MOVE_NORMAL) && (m->numbufferzones > 1) && (!m->corner_tested))
      {
        bool within_box = true;
        linedef *hitline;

        for (int j = 0; j < m->numhits; j++)
        {
          if (i != j)
          {
            hitline = m->hits[j].l;
            if ((hitline->from == l->from) || (hitline->from == l->to)
              || (hitline->to == l->from) || (hitline->to == l->to))
            {  // if the lines share a vertex (ie, form a corner), determine if actor is
              // inside of corner or outside of corner
              within_box = PointInBox(hitline, l, h->x, h->y);
              if (!within_box)
              { // if actor is outside of corner, pick which line to ignore
                if (LineDistance(h->x, h->y, hitline) < LineDistance(h->x,h->y,l))
                {
                  m->ignorecorner = hitline;
                  m->hits[j].bound = 0;
                }
                else
                {
                  m->ignorecorner = l;
                  h->bound = 0;
                }
              }
              m->corner_tested = true;
              break;
            }
          }
        }
        if ((!within_box) && (m->ignorecorner == l)) // if actor is outside of corner, quit processing this hit
          continue;
      }

      m->newx = h->x; // where we are moving too...
      m->newy = h->y;

      if (result)
      {
        result->dist = h->dist;
        result->l = l;
        if (l->actor)
          result->hit = HIT_ACTOR;
        else
        {
          if (m->ouractor->sector == l->facingsector)
            dest = level->Sectors[l->sector];
          else
            dest = level->Sectors[l->facingsector];

          if (((dest->FloorHt(h->x, h->y) + dest->HtOffset) <
            (level->Sectors[m->ouractor->sector]->FloorHt(h->x, h->y)
            + level->Sectors[m->ouractor->sector]->HtOffset
            + (.7*m->ouractor->physht)))
            && (m->ouractor->Type() == PLAYER))
            result->hit = HIT_JUMPABLE;
          else
            result->hit = HIT_WALL;
        }
      }

      // don't handle missile strikes unless we're actually moving
      if ((m->ouractor->Type() == MISSILE) && (m->move_type == MOVE_NORMAL))
      { // true value return from Collision means we're done moving the missile
        int collide_type;
        if (l->actor)
          collide_type = HIT_ACTOR;
        else
          collide_type = HIT_WALL;

        // if (l->actor && !(((cMissile*)m->ouractor)->Collision(HIT_ACTOR,l)))
          //return;

        /// TODO: FIX ME
        if (!(((cMissile*)m->ouractor)->Collision(collide_type,l)))
        {
            m->ouractor->x = h->x;
            m->ouractor->y = h->y;
            m->ouractor->x += 0.0f;//CosTable[FixAngle(m->ouractor->angle + Angle_180)]*32.0f;
            m->ouractor->y += 0.0f;//SinTable[FixAngle(m->ouractor->angle + Angle_180)]*32.0f;
        }
        return;

        h->slide_distance = 0.0f; // kill sliding
      }
      //eTB_Printf("m->newx: %f m->newy: %f hitx: %f hity: %f\n",m->newx,m->newx,h->x,h->y);
      //eTB_Printf("px: %f py: %f sx: %f sy: %f\n",player->X(),player->Y(),missile->x,missile->y);

      if (  (h->slide_distance != 0.0f)
        &&  (m->move_type == MOVE_NORMAL)
        && (m->slide_count < MAX_SLIDES)
        && ((abs((int)(m->ouractor->velocity))) == ((int)MAXWALK)))
      {  
        // recurse for slide, but only if we're actually moving,
        // and not if we're slowing down
        slideangle = GetFacingAngle(h->slide_x, h->slide_y, m->oldx, m->oldy);

        if ((abs(slideangle - m->origangle) > Angle_90) &&
          (abs(slideangle - m->origangle) < Angle_270))
        {
          //eTB_Printf("Slide-from: %.f, %.f to %.f, %.f\n", m->oldx, m->oldy, h->slide_x, h->slide_y);
          //eTB_Printf("Stopped: original angle is %i, slideangle is %i\n", m->origangle, slideangle);
          //eTB_Printf("Hit bound condition is %i, line bound condition is %i\n", h->bound, (h->l->flags & BOUND));
          break; // angle too severe, stop slide
        }

        if (!(PointInSector(m->newx,m->newy,level->Sectors[m->ouractor->sector])))
        {
          newsector = FindSector(m->newx, m->newy, m->ouractor->sector, false);
          if (newsector == DEAD_SECTOR)
            break;
          else
            m->ouractor->sector = newsector;
        }

        //eTB_Printf("slide # %d, from (%0f,%0f), staring with angle %d\n",m->slide_count,  m->newx,m->newy,m->moveangle);
        m->movedist = h->slide_distance;
        m->ouractor->x = m->oldx   = h->x;
        m->ouractor->y = m->oldy   = h->y;
        m->ouractor->sector = FindSector(m->ouractor->x, m->ouractor->y, m->ouractor->sector, false);
        m->newx   = h->slide_x;
        m->newy   = h->slide_y;
        m->moveangle = slideangle;
        //m->moveangle = GetFacingAngle(m->newx,m->newy,m->oldx,m->oldy);
        //eTB_Printf("slide # %d, to (%0f,%0f), going to angle%d\n",m->slide_count,  m->newx,m->newy,m->moveangle);
        m->slide_count++;
        VectorMove(m,NULL);
        return;
      }
      break;
    }
    else if (m->move_type == MOVE_NORMAL)
    {  // we are moving to a new sector, update actor's sector
      if (l->sector == m->ouractor->sector)
        m->ouractor->sector = l->facingsector;
      else
        m->ouractor->sector = l->sector;
    }
  }


  // in-sector ceiling height restriction
  dest = lvl->Sectors[m->ouractor->sector];
  if ((dest->CeilHt(m->newx, m->newy) - (dest->FloorHt(m->newx, m->newy)+dest->HtOffset)) <= m->ouractor->physht)
  { // won't fit
    m->newx = m->oldx;
    m->newy = m->oldy;
    if (result)
    {
      result->dist = 0.0f;
      result->l = NULL;
      result->hit = HIT_CEILING;
    }
  }

///
/// TODO: Fix Me
///
  // in-sector floor height restriction
  float mdx = BUFFER_DIST * 1.0f;//CosTable[m->moveangle];
  float mdy = BUFFER_DIST * 1.0f;//SinTable[m->moveangle];
  if ((PointInSector(m->oldx+mdx, m->oldy+mdy, dest))
    && ((dest->FloorHt(m->oldx+mdx, m->oldy+mdy)+dest->HtOffset) > (m->ouractor->z  - (m->ouractor->physht*.6))))
  { // too steep
    m->newx = m->oldx;
    m->newy = m->oldy;
    if (result)
    {
      result->dist = 0.0f;
      result->l = NULL;
      result->hit = HIT_FLOOR;
    }
  }



  if (m->move_type == MOVE_NORMAL)
  {  // move the actor...

    if (!(PointInSector(m->newx,m->newy,level->Sectors[m->ouractor->sector])))
    {
      newsector = FindSector(m->newx, m->newy, m->ouractor->sector, false);
      m->ouractor->x = m->newx;
      m->ouractor->y = m->newy;
      m->ouractor->sector = newsector;
    }
    else // no sector change, go to new spot
    {
        m->ouractor->x = m->newx;
        m->ouractor->y = m->newy;
    }
  }

  return;
}

//D3DXVector3 Direction;

/*
void
MoveActor (cli_parse::Actor* movactor, float angle, float distance, int move_type, move_result_t* result)
{
  move_params_t m;

  // fill in result
  if (result)
  {
    result->dist = 0.0f;
    result->hit  = NO_HIT;
    result->l    = NULL;
  }

  if (distance == 0.0f) return;

  m.ouractor  = movactor;
  m.moveangle = m.origangle = angle;

  if (distance < 0.0f)
  {
    distance = (float) fabs (distance);
    m.moveangle = m.origangle = angle; //(+180.0f);
    m.backwards = TRUE;
  }
  else
    m.backwards = FALSE;

  m.movedist = distance;

  D3DXVECTOR3 vDir;
  D3DXVECTOR3 vPos = m.ouractor->Camera ()->GetPos ();

  m.oldx  = vPos.x;
  m.oldy  = vPos.y;

  m.newx  = m.oldx + (vDir.x * m.movedist);
  m.newy  = m.oldy + (vDir.y * m.movedist);

  //eTB_Printf("moving dist = %f, x = %f, y = %f, m.newx = %f, m.newy = %f\n",m.movedist,m.oldx,m.oldy,m.newx,m.newy);
  m.slide_count   = 0;
  m.ignorecorner  = NULL;
  m.corner_tested = false;
  m.move_type     = move_type;

  VectorMove (&m, result);

  return;
}
*/
}
#endif

float last_last_x;
float last_last_y;
float last_last_z;

//#define ENHANCED_COLLISION


#define SMALL_NUM  0.00000001 // anything that avoids division overflow
// dot product (3D) which allows vector operations in arguments
#define dot(u,v)   ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
#define perp(u,v)  ((u).x * (v).y - (u).y * (v).x)  // perp product (2D)

class Segment
{
public:
  e3dVector3 P0;
  e3dVector3 P1;
};

//===================================================================

// inSegment(): determine if a point is inside a segment
//    Input:  a point P, and a collinear segment S
//    Return: 1 = P is inside S
//            0 = P is not inside S
int
inSegment (e3dVector3 P, Segment S)
{
    if (S.P0.x != S.P1.x) {    // S is not vertical
        if (S.P0.x <= P.x && P.x <= S.P1.x)
            return 1;
        if (S.P0.x >= P.x && P.x >= S.P1.x)
            return 1;
    }
    else {    // S is vertical, so test y coordinate
        if (S.P0.y <= P.y && P.y <= S.P1.y)
            return 1;
        if (S.P0.y >= P.y && P.y >= S.P1.y)
            return 1;
    }
    return 0;
}
//===================================================================


int
intersect2D_Segments (Segment S1, Segment S2, e3dVector3* I0, e3dVector3* I1)
{
    e3dVector3    u (S1.P1 - S1.P0);
    e3dVector3    v (S2.P1 - S2.P0);
    e3dVector3    w (S1.P0 - S2.P0);
    float     D = perp(u,v);

    // test if they are parallel (includes either being a point)
    if (fabs(D) < SMALL_NUM) {          // S1 and S2 are parallel
        if (perp(u,w) != 0 || perp(v,w) != 0) {
            return 0;                   // they are NOT collinear
        }
        // they are collinear or degenerate
        // check if they are degenerate points
        float du = dot(u,u);
        float dv = dot(v,v);
        if (du==0 && dv==0) {           // both segments are points
            if (S1.P0 != S2.P0)         // they are distinct points
                return 0;
            *I0 = S1.P0;                // they are the same point
            return 1;
        }
        if (du==0) {                    // S1 is a single point
            if (inSegment(S1.P0, S2) == 0)  // but is not in S2
                return 0;
            *I0 = S1.P0;
            return 1;
        }
        if (dv==0) {                    // S2 a single point
            if (inSegment(S2.P0, S1) == 0)  // but is not in S1
                return 0;
            *I0 = S2.P0;
            return 1;
        }
        // they are collinear segments - get overlap (or not)
        float t0, t1;                   // endpoints of S1 in eqn for S2
        e3dVector3 w2 = S1.P1 - S2.P0;
        if (v.x != 0) {
                t0 = w.x / v.x;
                t1 = w2.x / v.x;
        }
        else {
                t0 = w.y / v.y;
                t1 = w2.y / v.y;
        }
        if (t0 > t1) {                  // must have t0 smaller than t1
                float t=t0; t0=t1; t1=t;    // swap if not
        }
        if (t0 > 1 || t1 < 0) {
            return 0;     // NO overlap
        }
        t0 = t0<0? 0 : t0;              // clip to min 0
        t1 = t1>1? 1 : t1;              // clip to max 1
        if (t0 == t1) {                 // intersect is a point
            *I0 = S2.P0 + t0 * v;
            return 1;
        }

        // they overlap in a valid subsegment
        *I0 = S2.P0 + t0 * v;
        *I1 = S2.P0 + t1 * v;
        return 2;
    }

    // the segments are skew and may intersect in a point
    // get the intersect parameter for S1
    float     sI = perp(v,w) / D;
    if (sI < 0 || sI > 1)               // no intersect with S1
        return 0;

    // get the intersect parameter for S2
    float     tI = perp(u,w) / D;
    if (tI < 0 || tI > 1)               // no intersect with S2
        return 0;

    *I0 = S1.P0 + sI * u;               // compute S1 intersect point
    return 1;
}

void ClosestPoint_Point_vs_Segment (e3dVector3 c, Segment& segment, float& t, e3dVector3& d)
{
  e3dVector3& a = segment.P0;
  e3dVector3& b = segment.P1;

  e3dVector3 ab = b - a;

  t = (e3dVector3 (c - a) * (ab)) / (ab * ab);

  if (t < 0.0f) t = 0.0f;
  if (t > 1.0f) t = 1.0f;

  d = a + (t * ab);
}

bool
CorrectHeight (Underlight::Sector* sector, float x, float y, float& z)
{
  /* Using <= here, prevents oscillations when the player will not fit
      into a certain place... (i.e. head hits a sloping ceiling) */
  if ((z - 150.0f) <= sector->FloorHt (x, y)) {
    z = sector->FloorHt (x, y) + 150.0f;
    return true;
  }

  if ((z + 50.0f) > sector->CeilHt (x, y)) {
    z = sector->CeilHt (x, y) - 50.0f;
  }

  return false;
}

extern int
OnRight (Underlight::Line* aLine, float Px, float Py);

void
cli_parse::Player::Update (void)
{
#if 1
  if (states->cl_collision == false)
    return;

  bool test_bound = true;
  bool test_floors = true;
  
  static e3dVector3 vPos = cam->GetPos ();

  static float x = vPos.x;
  static float y = vPos.y;
  static float z = vPos.z;

  static float last_x = vPos.x;
  static float last_y = vPos.y;
  static float last_z = vPos.z;

  /* This is to keep us from shooting out of a room, in the
       event of a collision detection bug... */
  float last_last_x = last_x;
  float last_last_y = last_y;
  float last_last_z = last_z;

  vPos = cam->GetPos ();

  x = vPos.x;
  y = vPos.y;
  z = vPos.z;

  Underlight::Sector* last_sector = NULL;

  if (sector) {
     static Underlight::Room* last_room;
    Underlight::Room*         room = Underlight::room_from_sector (sector);

    /* On Room Change, ignore collisions... */
    if (last_room != room) {
      last_sector = NULL;
      last_room   = room;
      last_x      = x;
      last_y      = y;
      last_z      = z;
      return;
    } else {
      last_sector = sector;
    }

    last_room = room;
  } 

  sector    = Underlight::lvl->FindSector (x, y, sector, true);

  extern void
  CalcCrossedLines ( Underlight::Sector*              from_sec,
                     e3dVector3&                      from_pos,
                     e3dVector3&                      to_pos,
                     std::set <Underlight::Sector *>& sectors,
                     std::set <Underlight::Line *>&   lines );

  e3dVector3 from (last_x, last_y, 0.0f);
  e3dVector3 to   (x, y, 0.0f);

  std::set <Underlight::Sector *> sectors;
  std::set <Underlight::Line *>   lines;
  CalcCrossedLines (last_sector, from, to, sectors, lines);


  if (last_sector != NULL && lines.size () > 0) {
    ////eTB_Printf ("Move crosses %d sectors (%d lines)\n", sectors.size (), lines.size ());

    std::set <Underlight::Line *>::iterator line       = lines.begin ();
    std::set <Underlight::Line *>::iterator last_line  = lines.end   ();

    int times = 0;

    while (line != last_line) {
      e3dVector2 dir (x - last_x, y - last_y);
      e3dVector2 v1  ((*line)->x1 (), (*line)->y1 ());
      e3dVector2 v2  ((*line)->x2 (), (*line)->y2 ());

      /* Test for Ray-Line Segment Intersection */
      {
          Segment l_seg; /* Line Segment */
          Segment m_seg; /* Move Segment */

          l_seg.P0 = e3dVector3 ((*line)->x1 (), (*line)->y1 (), 0.0f);
          l_seg.P1 = e3dVector3 ((*line)->x2 (), (*line)->y2 (), 0.0f);

          m_seg.P0 = e3dVector3 (last_x, last_y, 0.0f);
          m_seg.P1 = e3dVector3 (x, y, 0.0f);

          float T;
          e3dVector3 FOOBAR;

         ClosestPoint_Point_vs_Segment (e3dVector3 (last_x, last_y, last_z),
                                          l_seg, T, FOOBAR); 
           
      /*
        float T = ((dir.x * last_y + dir.y *
                   (v1.x - last_x)) -
                   (dir.x * v2.y)) /
                   (dir.y * (v1.x + v2.x) -
                    dir.x * (v1.y + v2.y));
      */

        if (true) {
        //if (T >= 0.0f && T <= 1.0f) {
        //if (T >= 0.01f && T <= 0.99f) { // Gets rid of most false positives...
          e3dVector3 P0;
          e3dVector3 P1;


          int status = intersect2D_Segments (l_seg, m_seg, &P0, &P1);

          e3dVector3 vNormDir (dir.x, dir.y, 0.0f);
          vNormDir.Normalize ();

          e3dVector3 vLineNormal (P0.x * (*line)->COS  - P0.y * (*line)->SINE,
                                  P0.x * (*line)->SINE + P0.y * (*line)->COS,
                                  0.0000000f);

          vLineNormal.Normalize ();

          e3dVector3 vPoint (last_x, last_y, last_z);

          e3dVector3 vAngleBetween;
          vAngleBetween = vNormDir + vLineNormal;
          vAngleBetween.Normalize ();

      Underlight::Sector::Wall* fwall = (Underlight::Sector::Wall *)(*line)->fwall;
      Underlight::Sector::Wall* cwall = (Underlight::Sector::Wall *)(*line)->cwall;

          e3dVector3 origin (last_x, last_y, last_z);
          e3dVector3 velocity (dir.x, dir.y, 0.0f);

extern int
RayIntersectsTriangle (e3dVector3& p,  e3dVector3& d, 
                       e3dVector3& v0, e3dVector3& v1, e3dVector3& v2);



      eTB_Polygon* ceiling_poly = NULL;
      eTB_Polygon* floor_poly   = NULL;

      if (cwall != NULL) {
        ceiling_poly = cwall->polygon;

        if (ceiling_poly != NULL) {
          e3dVector3 t0 (*ceiling_poly->getVertex (0));
          e3dVector3 t1 (*ceiling_poly->getVertex (1));
          e3dVector3 t2 (*ceiling_poly->getVertex (2));
          e3dVector3 t3 (*ceiling_poly->getVertex (3));

          if ( RayIntersectsTriangle (origin, velocity, t0, t1, t2) ||
               RayIntersectsTriangle (origin, velocity, t2, t3, t0)    ) {
////            eTB_Printf ("Crosses Ceiling Wall... (%d)\n", (*line)->num);

              e3dVector3 tn ((t1 - t0) % (t2 - t0));
              tn.Normalize ();

              last_x = P0.x - tn.x * 5.0f;
              last_y = P0.y - tn.y * 5.0f;

x = last_x;
y = last_y;

              cam->SetPos (x, y, last_z);

              if (times < 10) {
              e3dVector3 from (x, y, 0.0f);
              e3dVector3 to   (x + vAngleBetween.x, vAngleBetween.y, 0.0f);

              last_sector = sector;
              sector = (*line)->facingsector;
              //sector = Underlight::lvl->FindSector (x, y, sector, true);

              sectors.clear ();
              lines.clear   ();

              CalcCrossedLines (sector, from, to, sectors, lines);

              line      = lines.begin ();
              last_line = lines.end ();

//              cam->SetPos (to.x, to.y, last_z);

              times++;
              continue;
              } else {
                ////last_x = x; last_y = y;
                return;
              }
          }
        }
      }


      if (fwall != NULL) {
        floor_poly = fwall->polygon;

        if (floor_poly != NULL && test_floors) {
          e3dVector3 t0 (*floor_poly->getVertex (0));
          e3dVector3 t1 (*floor_poly->getVertex (1));
          e3dVector3 t2 (*floor_poly->getVertex (2));
          e3dVector3 t3 (*floor_poly->getVertex (3));


          if ( RayIntersectsTriangle (origin, velocity, t0, t1, t2) ||
               RayIntersectsTriangle (origin, velocity, t2, t3, t0)    ) {

////            eTB_Printf ("Crosses Floor Wall... (%d)\n", (*line)->num);

              e3dVector3 tn ((t1 - t0) % (t2 - t0));
              tn.Normalize ();

              last_x = P0.x - tn.x * 5.0f;
              last_y = P0.y - tn.y * 5.0f;

x = last_x;
y = last_y;


//              x = P0.x;// - tn.x * 5.0f;//last_x;
//              y = P0.y;// - tn.y * 5.0f;//last_y;


////              eTB_Printf ("STOP!! Impassable line!\n");

              /* Without using recursion, this is a sufficient approximation
                   of a point before the collision... */

              //x += 0.75f * vAngleBetween.x;
              //y += 0.75f * vAngleBetween.y;

              cam->SetPos (x, y, last_z);

              if (times < 10) {
              e3dVector3 from (x, y, 0.0f);
              e3dVector3 to   (x + vAngleBetween.x, vAngleBetween.y, 0.0f);

              last_sector = sector;
              sector = (*line)->facingsector;
              //sector = Underlight::lvl->FindSector (x, y, sector, true);

              sectors.clear ();
              lines.clear   ();

              CalcCrossedLines (sector, from, to, sectors, lines);

              line      = lines.begin ();
              last_line = lines.end ();

//              cam->SetPos (to.x, to.y, last_z);

              times++;
              continue;
              } else {
                ////last_x = x; last_y = y;
                return;
              }

          }
        }
      }



          if ((*line)->flags & LINE_S_IMPASS && test_bound) {
            e3dVector3 bound_normal = vLineNormal;

            /* If this line has a floor wall or ceiling wall, use its
                 normal... this is more accurate.

                   FOR SOME REASON, line->COS and line->SINE are incorrect
                     for some linedefs. This is causing issues with texture
                       mapping and collision detection...
            */
            #if 0
            if (floor_poly || ceiling_poly) {
              eTB_Polygon* poly = floor_poly != null ? floor_poly : ceiling_poly;

              e3dVector3 t0 (*poly->getVertex (0));
              e3dVector3 t1 (*poly->getVertex (1));
              e3dVector3 t2 (*poly->getVertex (2));
              e3dVector3 t3 (*poly->getVertex (3));

              bound_normal = e3dVector3 ((t1 - t0) % (t2 - t0));
              bound_normal.Normalize ();
            }
            #endif

            e3dVector3 vTest (last_x, last_y, 0.0f);


            if (status == 1) {
              x = P0.x - bound_normal.x * 5.0f;
              y = P0.y - bound_normal.y * 5.0f;

              last_x = x;
              last_y = y;

////              eTB_Printf ("STOP!! Impassable line!\n");

              /* Without using recursion, this is a sufficient approximation
                   of a point before the collision... */

              //x += 0.75f * vAngleBetween.x;
              //y += 0.75f * vAngleBetween.y;

              cam->SetPos (x, y, last_z);

              if (times < 10) {
              e3dVector3 from (x, y, 0.0f);
              e3dVector3 to   (x + vAngleBetween.x, vAngleBetween.y, 0.0f);

              last_sector = sector;
              sector = (*line)->facingsector;
              //sector = Underlight::lvl->FindSector (x, y, sector, true);

              sectors.clear ();
              lines.clear   ();

              CalcCrossedLines (sector, from, to, sectors, lines);

              line      = lines.begin ();
              last_line = lines.end ();

//              cam->SetPos (to.x, to.y, last_z);

              times++;
              continue;
              } else {
                ////last_x = x; last_y = y;
                return;
              }
            }
          }

          /* If the requested move passes through this line, and
               this line is a "trip line", then ... */
          if ((*line)->num > 0         &&
              (T >= 0.01f && T <= 0.99f) &&
              (*line)->flags & TRIP_CROSS)
          {

                // Ignore lines with negative ids, or we will always trip
                // two lines at once... they are special cases, used for
                // tesselation only.

            /* Trip Line. */
            Underlight::TripLine* trip = (Underlight::TripLine *)(*line);

            switch (trip->action) {
              case Underlight::TripLine::Teleport:
              {
                  float angle = Underlight::UnderlightAngle (trip->trip4);
                  /*
                  eTB_Printf ( "Teleport: (%d, %d) - Rank Restriction: %d, "
                               "Angle: %3.1f\n",
                                 trip->trip1,
                                   trip->trip2,
                                     trip->trip3,
                                       angle );
                  */

                  /* The way teleporting works in cli_parse, we need to know
                       the room number before teleporting to an arbitrary
                         location... so find the room from (x,y). */
                  Underlight::Room* target_room = 
                    Underlight::room_from_pos (trip->trip1, trip->trip2);

                  if (target_room != NULL) {
                    Teleport (target_room->id, trip->trip1, trip->trip2, angle);
                    return; /* Skip any remaining movement logic, since we just
                                 invalidated our original position... :P */
                  } else {
                    eTB_Printf (" >> ERROR: Could not find the room associated"
                                " with this trip line (%d,%d)\n",
                                  trip->trip1, trip->trip2);
                  }
              } break;
              case Underlight::TripLine::Flip:
                break;
              case Underlight::TripLine::GoalPost:
                break;
              case Underlight::TripLine::LevelChange:
              {
                float angle = Underlight::UnderlightAngle (trip->trip4);

                char lvl_num [32];
                snprintf (lvl_num, 32, "cl_level_num %d", trip->trip4);
                ::states->command->ProcessCommandLine (lvl_num);

                /* The way teleporting works in cli_parse, we need to know
                     the room number before teleporting to an arbitrary
                       location... so find the room from (x,y). */
                Underlight::Room* target_room = 
                  Underlight::room_from_pos (trip->trip1, trip->trip2);

                if (target_room != NULL) {
                  Teleport (target_room->id, trip->trip1, trip->trip2, angle);
                  return; /* Skip any remaining movement logic, since we just
                               invalidated our original position... :P */
                } else {
                  eTB_Printf (" >> ERROR: Could not find the room associated"
                              " with a trip line's (x,y) [lvl change]...\n");
                }
              } break;
              case Underlight::TripLine::SoundEffect:
                  eTB_Printf ( "Play Sound: (%d, %d, %d, %d)\n",
                                 trip->trip1,
                                   trip->trip2,
                                     trip->trip3,
                                       trip->trip4 );

                break;
              case Underlight::TripLine::ShowMessage:
                  eTB_Printf ("Display Message: %d\n", trip->trip1);
                break;
              case Underlight::TripLine::QuestBuild:
                break;
              default:
                eTB_Printf (" >> Tripped a line with an unknown action type!\n");
                break;
            }
          }
        }
      }

      ++line;
    }
  }


  Underlight::Sector* ht_sec =
    Underlight::lvl->FindSector (x, y, sector, true);

  /* See comment for last_last_... above... */
  if (ht_sec == null) { //sector) {
    cam->SetPos (last_last_x, last_last_y, last_last_z);
    last_x = last_last_x;
    last_y = last_last_y;
    last_z = last_last_z;
    eTB_Printf ("Out of sector!\n");
    return;
  }


  on_ground = CorrectHeight (ht_sec, x, y, z);

  cam->SetPos (x, y, z);

  last_x = x;
  last_y = y;
  last_z = z;


#else
#if 0
  e3dVector3 vPos = cam->GetPos ();
  static float x = vPos.x;
  static float y = vPos.y;
  static float z = vPos.z;

  if (z != vPos.z)
    on_ground = false;

  float last_z = z;
  z = vPos.z;

  float last_x = x;
  float last_y = y;

  // True if the player hasn't moved since the last call to Update (...)
  bool xy_same = true;

  if (x != vPos.x ||
      y != vPos.y)
    xy_same = false;

  //if (x != vPos.x || y != vPos.y) {
  x = vPos.x;
  y = vPos.y;

  bool stay_on_ground = on_ground;

  /* Ignore gravity if collision detection is off ... */
  if (! states->cl_collision) {
    cam->SetPos (x, y, z);
    return;
  }

  // Skip the rest of the tests
  if (on_ground && xy_same)
    return;

  Underlight::Sector* last_sector = NULL;

  last_sector = sector;

  sector = Underlight::lvl->FindSector (x, y, sector, true);



#ifdef ENHANCED_COLLISION
  std::set <Underlight::Sector *> sectors;
  std::set <Underlight::Line *>   lines;
  CalcCrossedLines (last_sector, last_x, last_y, this, sectors, lines);

//  std::map < Underlight::Line*, std::pair < eTB_Polygon* floor, eTB_Polygon* ceiling > > lines_to_polys;

  if (lines.size () > 0) {
    eTB_Printf ("Move crosses %d sectors (%d lines)\n", sectors.size (), lines.size ());

    std::set <Underlight::Line *>::iterator line       = lines.begin ();
    std::set <Underlight::Line *>::iterator last_line  = lines.end   ();

    while (line != last_line) {
      Underlight::Sector::Wall* fwall = (Underlight::Sector::Wall *)(*line)->fwall;
      Underlight::Sector::Wall* cwall = (Underlight::Sector::Wall *)(*line)->cwall;

      e3dVector3 origin (    last_x,     last_y,     last_z);
      e3dVector3 dir    (x - last_x, y - last_y, z - last_z);

      if (fwall != NULL) {
        eTB_Polygon* floor_poly = fwall->polygon;

        if (floor_poly != NULL) {
          e3dVector3 t0 (*floor_poly->getVertex (0));
          e3dVector3 t1 (*floor_poly->getVertex (1));
          e3dVector3 t2 (*floor_poly->getVertex (2));
          e3dVector3 t3 (*floor_poly->getVertex (3));

          if ( RayIntersectsTriangle (origin, dir, t0, t1, t2) ||
               RayIntersectsTriangle (origin, dir, t2, t3, t0)    ) {
            eTB_Printf ("Crosses Floor Wall... (%d)\n", (*line)->num);
          }
        }
      }

      if (cwall != NULL) {
        eTB_Polygon* ceiling_poly = cwall->polygon;

        if (ceiling_poly != NULL) {
          e3dVector3 t0 (*ceiling_poly->getVertex (0));
          e3dVector3 t1 (*ceiling_poly->getVertex (1));
          e3dVector3 t2 (*ceiling_poly->getVertex (2));
          e3dVector3 t3 (*ceiling_poly->getVertex (3));

          if ( RayIntersectsTriangle (origin, dir, t0, t1, t2) ||
               RayIntersectsTriangle (origin, dir, t2, t3, t0)    ) {
            eTB_Printf ("Crosses Ceiling Wall... (%d)\n", (*line)->num);
          }
        }
      }

      ++line;
    }
  }
#endif

  /*
  if (last_sector != sector) {
    on_ground = false;
  }
  */

  if (! sector) {
    cam->SetPos (last_last_x, last_last_y, last_last_z);
  }

  if (sector) {
    Underlight::Room*   room = Underlight::room_from_sector (sector);
    //Underlight::Sector* sec  = room->GetSector              (sector);

    static Underlight::Room*   last_room;
    static Underlight::Sector* last_sec;

    if (last_room != room) {
      last_sec = NULL;
    }


  extern void
  CalcCrossedLines ( Underlight::Sector*              from_sec,
                     e3dVector3&                      from_pos,
                     e3dVector3&                      to_pos,
                     std::set <Underlight::Sector *>& sectors,
                     std::set <Underlight::Line *>&   lines );

  e3dVector3 from (last_x, last_y, 0.0f);
  e3dVector3 to   (x, y, 0.0f);

  std::set <Underlight::Sector *> sectors;
  std::set <Underlight::Line *>   lines;
  CalcCrossedLines (last_sec, from, to, sectors, lines);


  if (last_sec != NULL && lines.size () > 0) {
    ////eTB_Printf ("Move crosses %d sectors (%d lines)\n", sectors.size (), lines.size ());

    std::set <Underlight::Line *>::iterator line       = lines.begin ();
    std::set <Underlight::Line *>::iterator last_line  = lines.end   ();

int times = 0;

    while (line != last_line) {
      e3dVector2 dir (x - last_x, y - last_y);
      e3dVector2 v1  ((*line)->x1 (), (*line)->y1 ());
      e3dVector2 v2  ((*line)->x2 (), (*line)->y2 ());

      /* Test for Ray-Line Segment Intersection */
      {

        float T = ((dir.x * last_y + dir.y *
                   (v1.x - last_x)) -
                   (dir.x * v2.y)) /
                   (dir.y * (v1.x + v2.x) -
                    dir.x * (v1.y + v2.y));

        if (T >= 0.0f && T <= 1.0f) {
          if ((*line)->flags & LINE_S_IMPASS) {

            e3dVector3 P0;
            e3dVector3 P1;

            Segment l_seg; /* Line Segment */
            Segment m_seg; /* Move Segment */

            l_seg.P0 = e3dVector3 ((*line)->x1 (), (*line)->y1 (), 0.0f);
            l_seg.P1 = e3dVector3 ((*line)->x2 (), (*line)->y2 (), 0.0f);

            m_seg.P0 = e3dVector3 (last_x, last_y, 0.0f);
            m_seg.P1 = e3dVector3 (x, y, 0.0f);

            int status = intersect2D_Segments (l_seg, m_seg, &P0, &P1);

            if (status == 1 || status == 2) {
////              eTB_Printf ("STOP!! Impassable line!\n");

              /* Without using recursion, this is a sufficient approximation
                   of a point before the collision... */
              e3dVector3 vNormDir (dir.x, dir.y, 0.0f);
              vNormDir.Normalize ();
              x = P0.x - vNormDir.x * 1.25f;//last_x;
              y = P0.y - vNormDir.y * 1.25f;//last_y;

              e3dVector3 vLineNormal (P0.x * (*line)->COS  - P0.y * (*line)->SINE,
                                      P0.x * (*line)->SINE + P0.y * (*line)->COS,
                                      0.0000000f);

              vLineNormal.Normalize ();

#define CrossProduct(v1,v2,v3) {                       \
  (v3)[0] = ((v1 [1] * v2 [2])  -  (v1 [2] * v2 [1])); \
  (v3)[1] = ((v1 [2] * v2 [0])  -  (v1 [0] * v2 [2])); \
  (v3)[2] = ((v1 [0] * v2 [1])  -  (v1 [1] * v2 [0])); \
}

              e3dVector3 vAngleBetween;
              vAngleBetween = vNormDir + vLineNormal;
              vAngleBetween.Normalize ();
//              CrossProduct (e3dVector3 (dir.x, dir.y, 0.00001f), vLineNormal, vAngleBetween);// (-vNormDir, vLineNormal);

              //x += 0.75f * vAngleBetween.x;
              //y += 0.75f * vAngleBetween.y;

              cam->SetPos (x, y, last_z);

              if (times < 10) {
              e3dVector3 from (x, y, 0.0f);
              e3dVector3 to   (x + vAngleBetween.x, vAngleBetween.y, 0.0f);

              last_sector = sector;
              sector = (*line)->facingsector;
              sector = Underlight::lvl->FindSector (x, y, sector, true);

              sectors.clear ();
              lines.clear ();

              CalcCrossedLines (sector, from, to, sectors, lines);

              line = lines.begin ();
              last_line = lines.end ();

              cam->SetPos (to.x, to.y, last_z);

              times++;
              continue;
              } else {
                last_x = x; last_y = y;
                return;
              }
            }
          }

          if ((*line)->flags & TRIP_CROSS) {
            /* Trip Line. */
            Underlight::TripLine* trip = (Underlight::TripLine *)(*line);

            switch (trip->action) {
              case Underlight::TripLine::Teleport:
              {
                  float angle = Underlight::UnderlightAngle (trip->trip4);
                  eTB_Printf ( "Teleport: (%d, %d) - Rank Restriction: %d, "
                               "Angle: %3.1f\n",
                                 trip->trip1,
                                   trip->trip2,
                                     trip->trip3,
                                       angle );
              } break;
              case Underlight::TripLine::Flip:
                break;
              case Underlight::TripLine::GoalPost:
                break;
              case Underlight::TripLine::LevelChange:
                  eTB_Printf ( "Level Change: (%d, %d, %d, %d)\n",
                                 trip->trip1,
                                   trip->trip2,
                                     trip->trip3,
                                       trip->trip4 );
                break;
              case Underlight::TripLine::SoundEffect:
                  eTB_Printf ( "Play Sound: (%d, %d, %d, %d)\n",
                                 trip->trip1,
                                   trip->trip2,
                                     trip->trip3,
                                       trip->trip4 );

                break;
              case Underlight::TripLine::ShowMessage:
                  eTB_Printf ("Display Message: %d\n", trip->trip1);
                break;
              case Underlight::TripLine::QuestBuild:
                break;
              default:
                eTB_Printf (" >> Tripped a line with an unknown action type!\n");
                break;
            }
          }
        }
      }

        
      Underlight::Sector::Wall* fwall = (Underlight::Sector::Wall *)(*line)->fwall;
      Underlight::Sector::Wall* cwall = (Underlight::Sector::Wall *)(*line)->cwall;

/*
      if (fwall != NULL) {
        eTB_Polygon* floor_poly = fwall->polygon;

        if (floor_poly != NULL) {
          e3dVector3 t0 (*floor_poly->getVertex (0));
          e3dVector3 t1 (*floor_poly->getVertex (1));
          e3dVector3 t2 (*floor_poly->getVertex (2));
          e3dVector3 t3 (*floor_poly->getVertex (3));

          if ( RayIntersectsTriangle (origin, velocity, t0, t1, t2) ||
               RayIntersectsTriangle (origin, velocity, t2, t3, t0)    ) {
            e3dVector3 tn ((t1 - t0) % (t2 - t0));
            tn.Normalize ();
            velocity = velocity - tn * (tn * velocity) * 2.0f;
            velocity.Normalize ();
            new_pos = origin;
//            velocity = e3dVector3 (0.0f, 0.0f, 0.0f);
//            eTB_Printf ("Crosses Floor Wall... (%d)\n", (*line)->num);
          }
        }
      }

      if (cwall != NULL) {
        eTB_Polygon* ceiling_poly = cwall->polygon;

        if (ceiling_poly != NULL) {
          e3dVector3 t0 (*ceiling_poly->getVertex (0));
          e3dVector3 t1 (*ceiling_poly->getVertex (1));
          e3dVector3 t2 (*ceiling_poly->getVertex (2));
          e3dVector3 t3 (*ceiling_poly->getVertex (3));

          if ( RayIntersectsTriangle (origin, velocity, t0, t1, t2) ||
               RayIntersectsTriangle (origin, velocity, t2, t3, t0)    ) {
            e3dVector3 tn ((t1 - t0) % (t2 - t0));
            tn.Normalize ();
            velocity = velocity - tn * (tn * velocity) * 2.0f;
            velocity.Normalize ();
            new_pos = origin;
//            velocity = e3dVector3 (0.0f, 0.0f, 0.0f);
//            eTB_Printf ("Crosses Ceiling Wall... (%d)\n", (*line)->num);
          }
        }
      }
*/

      ++line;
    }
  }


//    float delta_z = (sec->FloorHt (x, y) + 150.0f) - z;

    /*
    if (stay_on_ground && delta_z > 100.0f) {
      z = sec->FloorHt (x, y) + 150.0f;
      cam->SetPos (x, y, z);
      on_ground = true;
    } else {
    */

    if (sector) {
      /*
      if (sec->SlopingFloor ()) {
        if (! xy_same)
          on_ground = false;
      }
      */

      if (last_sec) {
        if (z - sector->FloorHt (x, y) < -1.0f ||
            z - sector->CeilHt  (x, y) >  1.0f) {// ||
         //last_sec->FloorHt (last_x, last_y) - sector->FloorHt (x, y) >  100.0f) {
          if (sector->FloorHt (last_x, last_y) == last_sec->FloorHt (x, y)) {
            on_ground = true;
          }

          cam->SetPos (last_last_x, last_last_y, last_last_z);
          sector = last_sec;
          return;
        }

        if (sector->FloorHt (last_x, last_y) != last_sec->FloorHt (x, y)) {
          on_ground = false;
        }
      }

      if (! on_ground) {
        if ((z - 150.0f) < sector->FloorHt (x, y)) {
          z = sector->FloorHt (x, y) + 150.0f;
          cam->SetPos (x, y, z);
          on_ground = true;
        }
      }

      if ((z + 50.0f) > sector->CeilHt (x, y)) {
        z = sector->CeilHt (x, y) - 50.0f;
        cam->SetPos (x, y, z);
      }
    }
    //}

    last_sec  = sector;
    last_room = room;
  } else {
    // Don't allow gravity when not in a sector...
    cam->SetPos (x, y, last_z);
    on_ground = true;
  }
  //}

  last_last_x = x;
  last_last_y = y;
  last_last_z = z;
#endif
#endif
}


bool
cli_parse::Player::OnGround (void)
{
  return on_ground;
}


float
cli_parse::Player::GetX (void) const
{
  return cam->GetPos ().x;
}

float
cli_parse::Player::GetY (void) const
{
  return cam->GetPos ().y;
}

float
cli_parse::Player::GetZ (void) const
{
  return cam->GetPos ().z;
}

int
cli_parse::Player::GetRoom (void) const
{
  return room;
}

#include "../../Epsilon/debug.h" // eMessageBox

float
cli_parse::Player::GetAngle (void) const
{
  eMessageBox (_T ("Please implement cli_parse::Player::GetAngle (...)"));
  return 0.0f;
}

size_t
cli_parse::Player::MemSize (void) const
{
  return sizeof (cli_parse::Player);
}
